"""
合同签约处理脚本

功能：
- 输入 SubDetailItem → 调用通义千问 → 生成结构化 JSON → 校验并入库 TransactionResult
"""

from typing import Dict, Iterable, List, Optional
from spider.models import SubDetailItem
from process.ai.unified import chat_with_usage
from deal.models import TransactionResult, Project, Company
from django.utils import timezone
from decimal import Decimal
import json
import re
from datetime import datetime, date


DEFAULT_SYSTEM_MSG = (
    "你是一名合同签约信息结构化抽取专家。"
)

DEFAULT_INSTRUCTION = (
    "从页面中识别合同签约的核心字段并结构化输出。"
)

DEFAULT_OUTPUT_FORMAT = (
    "输出 JSON：\n"
    "sign_date：签约日期（YYYY-MM-DD，无法确定则 null）；\n"
    "contract_code：合同编号（字符串，若不存在则生成空字符串）；\n"
    "contract_amount：合同金额（单位元，Decimal 语义，无法确定则 null）；\n"
    "company_name：签约公司名称（用于关联 Company，无法确定则 null）；\n"
    "project_code：项目编号（用于关联 Project，无法确定则 null）。"
)


def _parse_json(text: str) -> Dict:
    if not text:
        return {}
    s = text.strip()
    if s.startswith("```"):
        s = s.split("\n", 1)[1] if "\n" in s else s
        if s.endswith("```"):
            s = s[:-3].strip()
    s = re.sub(r"^\s*<think>[\s\S]*?</think>\s*", "", s)
    m = re.search(r"\{[\s\S]*\}", s)
    if m:
        frag = m.group(0)
        try:
            return json.loads(frag)
        except Exception:
            pass
    try:
        return json.loads(s)
    except Exception:
        return {}


def _parse_date(value: str | None) -> Optional[date]:
    if not value:
        return None
    value = value.strip()
    for fmt in ("%Y-%m-%d", "%Y/%m/%d"):
        try:
            return datetime.strptime(value, fmt).date()
        except Exception:
            continue
    try:
        return datetime.fromisoformat(value).date()
    except Exception:
        return None


def _parse_amount(value) -> Optional[Decimal]:
    if value is None:
        return None
    try:
        if isinstance(value, (int, float, Decimal)):
            return Decimal(str(value))
        s = str(value).strip().replace(",", "")
        return Decimal(s)
    except Exception:
        return None


def build_messages(
    item: SubDetailItem,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    system: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
    json_payload: Optional[Dict] = None,
) -> List[Dict[str, str]]:
    sys_msg = system or DEFAULT_SYSTEM_MSG
    inst = instruction or DEFAULT_INSTRUCTION
    fmt = output_format or DEFAULT_OUTPUT_FORMAT
    ctx = extra_context or {}
    payload = json_payload if json_payload is not None else {
        "url": item.url,
        "html_content": item.html_content,
    }
    content = (
        "Instruction:\n" + inst + "\n" +
        "OutputFormat:\n" + fmt + "\n" +
        "Context:\n" + json.dumps(ctx, ensure_ascii=False) + "\n" +
        "URL:\n" + item.url + "\n" +
        "JSON:\n" + json.dumps(payload, ensure_ascii=False)
    )
    return [
        {"role": "system", "content": sys_msg},
        {"role": "user", "content": content},
    ]


def process_transaction(
    item: SubDetailItem,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    system: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
    model: str = "qwen-plus",
    stream: bool = False,
    json_payload: Optional[Dict] = None,
    **kwargs,
) -> str | Iterable[str]:
    messages = build_messages(item, instruction, output_format, system, extra_context, json_payload=json_payload)
    return chat_with_usage(messages=messages, model=model, **kwargs).get("content")


def process_and_ingest_transaction(
    item: SubDetailItem,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    system: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
    model: str = "qwen-plus",
    json_payload: Optional[Dict] = None,
    **kwargs,
) -> Dict[str, object]:
    r = chat_with_usage(
        messages=build_messages(
            item=item,
            instruction=instruction,
            output_format=output_format,
            system=system,
            extra_context=extra_context,
            json_payload=json_payload,
        ),
        model=model,
        **kwargs,
    )
    payload = _parse_json(r.get("content") or "")
    errors: List[str] = []
    if not payload:
        return {"ok": False, "errors": ["结构化输出不是合法 JSON"]}

    sign_date = _parse_date(payload.get("sign_date"))
    if not sign_date:
        errors.append("签约日期解析失败：sign_date")

    contract_code = payload.get("contract_code") or ""
    contract_amount = _parse_amount(payload.get("contract_amount"))
    if contract_amount is None:
        errors.append("合同金额解析失败或缺失：contract_amount")

    company_name = payload.get("company_name") or "未知公司"
    company = Company.objects.filter(company_name=company_name).first()
    if not company:
        from uuid import uuid4
        company = Company.objects.create(
            company_name=company_name,
            unified_social_credit_code=f"AUTO-{uuid4().hex[:12]}",
            company_info={},
            qualification_info={},
        )

    project_code = payload.get("project_code") or f"AUTO-{item.task_id}-{item.id}"
    project = Project.objects.filter(project_code=project_code).first()
    if not project:
        project = Project.objects.create(
            project_code=project_code,
            project_name=payload.get("project_name") or "未命名项目",
            owner_name="未知业主",
            proxy_company="",
            project_type="",
            service_period="",
            budget_amount=None,
            extra_data={},
            crawl_task_id=item.task_id,
        )

    if errors:
        return {"ok": False, "errors": errors}

    # 合同唯一：合同编号（若为空则按公司+项目+日期近似）
    existing = None
    if contract_code:
        existing = TransactionResult.objects.filter(contract_code=contract_code).first()
    if existing:
        existing.sign_date = sign_date
        existing.contract_amount = contract_amount
        existing.company = company
        existing.project = project
        existing.crawl_task_id = item.task_id
        existing.save()
        if item.task_id and item.task:
            item.task.status = "crawled"
            item.task.crawled_at = timezone.now()
            item.task.save(update_fields=["status", "crawled_at"]) 
        # 合同签约：若金额缺失也标记
        if contract_amount is None or contract_amount <= 0:
            item.is_suspect = True
            item.save(update_fields=["is_suspect"]) 
        return {
            "ok": True,
            "id": existing.id,
            "action": "updated",
            "summary": {
                "url": original_url,
                "project_name": project.project_name,
                "units": [company.company_name],
                "amount": str(contract_amount) if contract_amount is not None else None,
            },
            "usage": r.get("usage") or {},
        }

    created = TransactionResult.objects.create(
        sign_date=sign_date,
        contract_code=contract_code or f"AUTO-{company_name}-{project_code}-{sign_date}",
        contract_amount=contract_amount,
        company=company,
        project=project,
        crawl_task_id=item.task_id,
    )
    if item.task_id and item.task:
        item.task.status = "crawled"
        item.task.crawled_at = timezone.now()
        item.task.save(update_fields=["status", "crawled_at"]) 
    if contract_amount is None or contract_amount <= 0:
        item.is_suspect = True
        item.save(update_fields=["is_suspect"]) 
    return {
        "ok": True,
        "id": created.id,
        "action": "created",
        "summary": {
            "url": original_url,
            "project_name": project.project_name,
            "units": [company.company_name],
            "amount": str(contract_amount) if contract_amount is not None else None,
        },
        "usage": r.get("usage") or {},
    }


__all__ = [
    "build_messages",
    "process_transaction",
    "process_and_ingest_transaction",
]
